#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "../inc/scanner.h"
#include "../inc/command.h"
#include "../inc/defs.h"
#include "../inc/log.h"

#define MAX_LONG_COMMAND	100

/* Funciones privadas */
static void freeArgs(char ** args, int cant);

typedef struct commandEntryT
{
	char *name;					/* Nombre del comando */
	int cantTokCommand;			/* Cantidad de tokens que componen el comando */
	int cantTokParam;			/* Cantidad de tokens de los parametros */
	int	cantTokOpt;				/* Cantidad de tokens de los parametros opcionales */
	commandFn fn;				/* Puntero a funcion comando a ejecutar */
	struct commandEntryT *next;	/* Proximo nodo comando */
} commandEntryT;

struct commandCDT
{
	commandEntryT *nodoIni;	/* nodo inicial */
	commandEntryT *nodoActual; /* nodo actual */
	int cantCommand;		/* Cantidad de comandos definidos */
};

commandADT
NewCommand(void)
{
	commandADT command;

	/* Creo tipo de dato oculto */
	if ( (command = calloc(1, sizeof(struct commandCDT))) == NULL )
	{
		Fatal("NewCommand@commandADT.c - Not enough memory to create command\n");
		return NULL;
	}

	return command;
}

int
InsertCommand(commandADT command, char *nameCommand, commandFn fn, int cantTokCommand, int cantTokParam, int cantTokOpt)
{
	commandEntryT *nodo, *aux;

	if ( Precondicion(command) || Precondicion(nameCommand)
			|| Precondicion(fn) || cantTokCommand < 0  || cantTokParam < 0)
	{
		Error("InsertCommand@commandADT.c - Invalid arguments\n");
		return 0;
	}

	/* Aloco el nuevo nodo de comandos */
	if ( (nodo = calloc(1,sizeof(commandEntryT))) == NULL )
	{
		Fatal("InsertCommand@commandADT.c - Not enough memory to insert a new command\n");
		return 0;
	}

	/* Me fijo si es el primer nodo */
	if (command->cantCommand == 0)
	{
		command->nodoIni = command->nodoActual = nodo;
	}
	else
	{
		aux = command->nodoIni;
		while(aux->next != NULL) /* Busco el ultimo nodo */
			aux = aux->next;

		aux->next = nodo; /* seteo el puntero al ultimo nodo que voy a insertar */
	}

	nodo->name = nameCommand;
	nodo->fn = fn;
	nodo->next = NULL;
	nodo->cantTokCommand = cantTokCommand;
	nodo->cantTokParam = cantTokParam;
	nodo ->cantTokOpt = cantTokOpt;

	command->cantCommand++;

	return 1;
}

void
SetBeginCommand(commandADT command)
{
	if ( Precondicion(command) )
	{
		Error("SetBeginCommand@commandADT.c - Invalid argument\n");
		return;
	}

	command->nodoActual = command->nodoIni;
}

int
GetNextCommand(commandADT command, char **name, int *cantTokCommand, int *cantTokParam, int * cantTokOpt, commandFn *fn)
{
	if ( Precondicion(command) || Precondicion(name)
			|| Precondicion(cantTokCommand) || Precondicion(cantTokParam)
			|| Precondicion(fn) )
	{
		Error("GetNextCommand@commandADT.c - Invalid arguments\n");
		return 0;
	}

	if (command->nodoActual == NULL)
		return 0;

	/* Le paso al usuario los datos del nodo actual */
	*name = command->nodoActual->name;
	*cantTokCommand = command->nodoActual->cantTokCommand;
	*cantTokParam = command->nodoActual->cantTokParam;
	*cantTokOpt = command -> nodoActual -> cantTokOpt;
	*fn = command->nodoActual->fn;

	command->nodoActual = command->nodoActual->next; /* Apunto al proximo nodo */

	return 1;
}

int
parseCommand(commandADT command, char *commandLine)
{
	int cantRead=0, end=0, endOk=0, i, next, notfound = 0;
	int cantTokCommandAux, cantTokParamAux = 0, cantOpt;
	char cmdstring[MAX_LONG_COMMAND] = {0};
	char **param = NULL;
	char *tok;
	char ** aux;
	commandFn fnaux;
	scannerADT scanner;

	if ( Precondicion(command) || Precondicion(commandLine))
	{
		Error("parseCommand@commandADT.c - Invalid arguments\n");
		return -1;
	}

	if ( strlen(commandLine) >= MAX_LONG_COMMAND )
	{
		Error("parseCommand@commandADT.c - command size overflow\n");
		return 0;
	}

	if ( Precondicion((scanner = NewScanner())) ) /* creo el scanner */
	{
		Fatal("parseCommand@commandADT.c - Not enough memory\n");
		Debug("parseCommand@commandADT.c - Scanner instance not initialized\n");
		return -1;
	}

	if ( (aux = calloc(1, sizeof(char *))) == NULL )
	{
		Fatal("parseCommand@commandADT.c - Not enough memory\n");
		return -1;
	}

	SetScannerString(scanner, commandLine); /* Inicializo string para separarlo en tokens */

	while( !end && (next=GetNextCommand(command, aux, &cantTokCommandAux,&cantTokParamAux, &cantOpt, &fnaux)))
	{
		/* Armo el string de comando a validar */
		for (i=0; i<(cantTokCommandAux-cantRead); i++)
		{
			if ( !MoreTokensExist(scanner) )
			{
				free(aux);
				FreeScanner(scanner);
				return 0; /* Comando invalido  */
			}

			tok = ReadToken(scanner);
			cantRead++;
			
			if (cantTokCommandAux != 1)
				strcat(cmdstring, " ");
				
			strcat(cmdstring, tok);
			free(tok);
			strcat(cmdstring, " ");
		}

		if (i != 0)
			cmdstring[strlen(cmdstring)-1] = '\0'; /* borra el ultimo espacio del final */

		if ( (notfound = strcmp(cmdstring, *aux)) == 0) /* Comando valido */
		{
			if ( (param = calloc(cantTokParamAux, sizeof(char *))) == NULL )
			{
				free(aux);
				FreeScanner(scanner);
				return -1; /* ERROR DE MEMORIA */
			}

			for (i=0; i<cantTokParamAux; i++)
			{
				if ( !MoreTokensExist(scanner) )
				{
					/* Si no hay mas tokens pero la cantidad de argumentos leida
					 * es de al menos TODOS los argumentos obligatorios, no es un error!
					 */
					if ( (cantTokParamAux - i) <= cantOpt )
						break; 
					
					FreeScanner(scanner); /* Libera el scanner */
					freeArgs(param, cantTokParamAux);
					free(aux);
					return 0; /* Comando invalido  */
				}

				tok = ReadToken(scanner);
				if ( (param[i] = malloc(sizeof(char)*(strlen(tok)+1))) == NULL )
				{
					freeArgs(param, cantTokParamAux);
					free(tok);
					free(aux);
					FreeScanner(scanner);
					return -1; /* ERROR DE MEMORIA */
				}

				strcpy(param[i], tok);
				free(tok);
			}

			if ( MoreTokensExist(scanner) ) /* Si quedaron mas parametros es un comando invalido */
			{
				endOk = 0;
				break;
			}

			endOk = fnaux(cantTokParamAux, param); /* Ejecuto la funcion destinada al comando */
			end = 1;
		}
	}

	FreeScanner(scanner); /* Libero el scanner */
	
	if(!notfound)
		freeArgs(param, cantTokParamAux);

	/* Si se chequearon todos los comandos validos
	 * y ninguno macheo con el buscado, es invalido */
	if (next == 0)
		endOk = 0;

	free(aux);
	
	return endOk;
}


int
DeleteCommand(commandADT command, char *nameCommand)
{
	commandEntryT *nodo, *ant;

	if ( Precondicion(command) || Precondicion(nameCommand) )
	{
		Error("DeleteCommand@commandADT.c - Invalid arguments\n");
		return 0;
	}

	if (command->nodoIni == NULL)
	{
		Error("DeleteCommand@commandADT.c - You don't have any node to delete.\n");
		return 0;
	}

	ant = nodo = command->nodoIni;
	while(nodo->next != NULL)
	{
		if ( !strcmp(nameCommand, nodo->name) ) /* busco el nodo comando a borrar */
		{
			if (nodo == command->nodoActual)
				command->nodoActual = nodo->next;

			if (nodo == ant) /* caso del primer nodo de todos */
				command->nodoIni = nodo->next;
			else
				ant->next = nodo->next;

			free(nodo); /* lo borro */

			Notification("El comando se elimino con exito\n");
			return 1;
		}

		ant = nodo;
		nodo = nodo->next;	/* itero por el proximo nodo */
	}
	Notification("No se encontro a dicho comando para eliminarlo\n");
	return 0;
}

void
FreeCommand(commandADT command)
{
	commandEntryT *nodo, *aux;

	if ( Precondicion(command) )
	{
		Error("FreeCommand@commandADT.c - Invalid argument\n");
		return;
	}

	nodo = command->nodoIni;

	while (nodo != NULL)
	{
		aux = nodo->next;
		free(nodo);
		nodo = aux;
	}

	free(command);
}

static void
freeArgs(char ** args, int cant)
{
	int i ;

	for (i = 0 ; i < cant ; i++)
		free(args[i]);

	free(args);
	return;
}
